Source file: /~heha/basteln/PC/Programmiergeräte/Wild GIF12/mtp3.zip/MTP.cpp

#include "MTP.h"
#include "usb.h"
#include <string.h>
#include <avr/eeprom.h>
#include <avr/pgmspace.h>

struct MTPHeader {
 union{
  byte lenL;  // 0
  word lenW;
  uint32_t lenD;
  byte lenB[4];
 };
 byte typeL,typeH; // 4
 byte opL,opH;   // 6
 uint32_t transid; // 8
};

static struct{
 MTPHeader h;
 uint32_t params[3];    // 12
}bulkcs;

word eefill;	// Füllstand des EEPROM = Dateilänge

namespace MTP {

static void outc(const byte*p,byte len) {
 usbSend(p,len);
}
static void out1(byte b) {
 outc(&b,1);
}
static void out2(word w) {
 out1(w);
 out1(w>>8);
}
static void out4(word w) {
 out2(w);
 out2(0);
}
static void out0(byte len) {
 usbSend(0,len);
}
static void out8(word w) {
 out2(w);
 out0(6);
}
static void outHeader(word len) {
 out4(len+12);
 out2(2);		// type
 outc(&bulkcs.h.opL,6);	// operation + transaction ID
}
static void outFlush() {
 usbFlush();
}

static void outDescriptor() {
 outHeader(11+10+20);
 out0(11);
 out4(3);	// Only needed capabilities (Windows 10)
 out2(0x100B);  // DeleteObject		// required otherwise files are deletion protected, and write protected after copying to local storage
 out2(0x100C);  // SendObjectInfo	// required otherwise files cannot be added
 out2(0x100D);  // SendObject		// same as above
 out0(20);
 outFlush();
}

static void outStorageIDs() {
 outHeader(8);
 out4(1); // 1 entry
 out4(1); // 1 storage
 outFlush();
}

static void outStorageInfo() {
 outHeader(2+2+2+8+8+4+1+1);
 out2(3);	// storage type (fixed RAM)
 out2(1);	// filesystem type (generic flat)
 out2(0);	// access capability (read-write)
 out8(1024);	// max capacity (bytes)
 out8(1024-eefill);	// free space (bytes)
 out4(0);	// free space (objects)
 out0(2);	// 2 identifier, jetzt kommt "Festspeicher"
 outFlush();
}

static void outObjectHandles() {
 outHeader(8);
 out4(1);
 out4(1);
 outFlush();
}

static void outObjectInfo(byte) {
 static const PROGMEM char name[]="eeprom";
 outHeader(4+4+4+40+1+(sizeof name<<1)+3);
 out4(1); // Logical Storage ID
 out4(0x3000);	// undefiniert
 out4(eefill); // size
 out0(40); // Info für Bilder
 out1(sizeof name);
 for (byte i=0;i<sizeof name;i++) out2(pgm_read_byte(name+i));
 out0(3);  // date captured, date modified, keywords: all undefined
 outFlush();
}

static void GetObject(byte) {
 outHeader(eefill);
 for (word i=0; i<eefill; i++) out1(eeprom_read_byte((byte*)i));
 outFlush();	// Short-Packet
}

static void usbSkipString() {
 byte n;
 usbRecv(&n,1);
 usbRecv(0,n+n);
}

static bool SendObjectInfo() {
 usbRecv(0,20);	// Header, Dateityp ignorieren
 usbRecv(&eefill,2);
 usbRecv(0,42);	// Krimskrams dazwischen ignorieren
 usbSkipString();	// name
 usbSkipString();	// ctime
 usbSkipString();	// mtime
 usbSkipString();	// keywords
 return eefill<=1024;
}

// Datei überschreiben: Jetzt kommen die Daten, und Dateilänge sollte gleich sein.
static bool SendObject() {
 word size;
 usbRecv(&size,2);
 usbRecv(0,10);
 size-=12;
 if (size!=eefill) return false;
 for (word i=0; i<size; i++) {
  byte b;
  usbRecv(&b,1);
  eeprom_write_byte((byte*)i,b);
 }
 return true;
}

static void handleCommand() {
 byte p1 = 0;
 byte retcode = 1;  // Ok
 if (bulkcs.h.opH!=0x10) retcode=5;
 else switch (bulkcs.h.opL) {
  case 1: outDescriptor();		break;	// GetDescription
  case 2: break;	// OpenSession
  case 3: break;	// CloseSession
  case 4: outStorageIDs();		break;	// GetStorageIDs
  case 5: outStorageInfo();		break;	// GetStorageInfo
  case 6: p1 = 1;			break;	// GetNumObjects
  case 7: outObjectHandles();		break;	// GetObjectHandles
  case 8: outObjectInfo(bulkcs.params[0]);break;// GetObjectInfo
  case 9: GetObject(bulkcs.params[0]);	break;	// GetObject
  case 11: break; // DeleteObject
  case 12:	// SendObjectInfo
  if (!SendObjectInfo()) retcode=12;	// store_fill
  else bulkcs.params[2] = 1/*handle*/;
  bulkcs.h.lenL = 12 + 3*4;	// 3 Parameter
  goto ex1;
  case 13:	// SendObject
  if (!SendObject()) retcode=21;	// no_valid_objectinfo
  bulkcs.h.lenL = 12;
// Events werden (zumindest unter Windows 10) nun doch nicht mehr benötigt.
// Kann man sich also sparen.
  break;
  default:
  retcode = 5;  // operation not supported
 }
 bulkcs.h.lenL = 16;	// Standard: Rückgabe 1 Parameter 
 bulkcs.params[0] = p1;
ex1:			// für mehr als 1 Parameter
 bulkcs.h.typeL = 3;	// Antwort
 bulkcs.h.opL = retcode;
 bulkcs.h.opH = 0x20;
 usbSend(&bulkcs,bulkcs.h.lenL);
 usbFlush();
}

// Einzige exportierte Funktion
void pollUSB() {
 if (usbRecvPoll()) {
  PORTD&=~0x20;	// TX
  usbRecv(&bulkcs,8);
  if (bulkcs.h.typeL==1	// "command", alles andere: STALL (Protokollfehler)
//  && bulkcs.h.typeH==0x10
  && bulkcs.h.lenD<=sizeof bulkcs
  && bulkcs.h.lenL>=12) {
   usbRecv(&bulkcs.h.transid,bulkcs.h.lenL-8);	// Rest des Kommandos abholen
   handleCommand();
  }else{
//   eelog(&bulkcs,8);	// Murks!
   UEINTX=0;		// aufs nächste ganze USB-Paket synchronisieren
  }
  PORTD|= 0x20;
 }
}

}/*namespace*/
Detected encoding: UTF-80